package docmodel;


import java.io.File;
import java.io.IOException;

import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.apache.xerces.dom.DocumentImpl;
import org.apache.xerces.dom.events.MutationEventImpl;
import org.apache.xerces.parsers.DOMParser;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.w3c.dom.events.Event;
import org.w3c.dom.events.EventListener;
import org.w3c.dom.events.EventTarget;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
//http://xerces.apache.org/xerces-j/features.html
//http://xerces.apache.org/xerces-j/properties.html
//http://xerces.apache.org/xerces-j/schema.html
//Parser 1: 
//import javax.xml.parsers.SAXParserFactory;
//import javax.xml.parsers.SAXParser; //SAX
//Parser 2: XERCES
//DOM														
//import org.apache.xerces.parsers.SAXParser; //SAX

/**
 * 
 * @author andrea
 *
 */
public class XDP_Reader implements EventListener, XDP_Reader_Interface  
{	
	/** OBJ */
	private XsdTool xsd_tool; //TODO rinomina in XSD_Writer
	
	/** OBJ */
	private XDP_Liste liste;

	/** */
	private DOMParser parser;
	
	/** */
	private Document doc;	 
	
	private Element root;
	
	/** */
	private String inputFile;
	
	/** */
	//private String outputFile;
	
	/** buffer del log */
	private StringBuffer log;
	

	/**
	 * COSTRUTTORE  
	 * 
	 * @param filename
	 * 
	 */
	public XDP_Reader (String filename) 
	{
		//TODO check file name
		//TODO check file existenz
		
		inputFile = filename;
	
		log      = new StringBuffer();
		liste  	 = new XDP_Liste();
		
		//xsd_tool = new XsdTool(inputFile);								
		xsd_tool = new XsdTool(this);								
	}

	
	//------------------------------------------- PRIVATE Methods --------------------------------------
	
	/**
	 * 
	 * @param theNode
	 */
	private void scorriNodo  (Node theNode) 
	{
		NodeList children = theNode.getChildNodes();
	    
		//
		stampaNodo(theNode);
	    
	    for (int i = 0; i < children.getLength(); i++) 
	    {
	    	Node aNode = children.item(i);
	    	
	    	if (aNode.hasChildNodes()){
	    		if(aNode.getNodeName().equals("span")) {
	    			NamedNodeMap attrs = aNode.getAttributes();
	    			Node style= attrs.getNamedItem("style");
	    			if(style!=null && style.getNodeValue().contains("color:#ff0000;")){
	    				style.setNodeValue("");
	    			}
	    		}
	    		scorriNodo(aNode);
	    	}
	    	else 
	    	{
//	    		String name = aNode.getNodeName();
//	    		String value = aNode.getNodeValue(); 
	    		
	    		stampaNodo(aNode);
	    		
	    		if( is_textNode(aNode) ) 
	    		{	
	    			cercaMarker(aNode);	
	    			//log.append("\n" +"nodo testo TROVATO");
	    		}
	    		
	    			
	    	}
	    }
	}
	
    /**
     * 
     * @param aNode
     */
	private void stampaNodo(Node aNode)
	{
	    String name = aNode.getNodeName();
	    String value = aNode.getNodeValue(); 
	    
	    if(value != null && !value.trim().equals("")) 
	    	log.append("\n" +name + "," + value);
	}

	
	/**
	 * 
	 * @param aNode
	 * @return
	 */
	private boolean is_textNode(Node aNode) {
		int tipoNodo = aNode.getNodeType();
		if(tipoNodo != Node.TEXT_NODE)
			return false;	
		return true;
	}
   
	/**
	 * 
	 * @param aNode
	 */
	private void cercaMarker(Node aNode) 
	{
		/*String text = aNode.getTextContent();
	
		log.append("\n" + 	"#Nome nodo: "+aNode.getNodeName() 
							+ 	", valore originale: "
							+	text 
							+	" | Valore sostituito: " 
							+ 	Marker.findMarker(text,null)); */
		Marker.setLogBuffer(log);
		Marker.findNodeMarker(aNode, liste);														//Marker.INSTANCE.findNodeMarker(aNode, liste);			
	}
	
	/**
	 * 
	 */
	private void add_FloatingFields () 
	{
		Node pageArea = doc.getElementsByTagName("pageArea").item(0);
		for (Field ff : liste.listaFields) {
			pageArea.appendChild(ff.getFieldElement(doc));
		}
	}

	/**
	 * 
	 */
	private void add_ConnectionSet() 
	{		
		//</x:xmpmeta>	*** appended elements goes here *** </xdp:xdp>
			
		Element connectionSet, xsdConnection, uri, rootElement;
		Text uri_text_node,rootElement_text_node;
		
		String name_attr 	= "name", dataDescription_attr = "dataDescription";
		String file_xsd 	= xsd_tool.getOutput_file_name_only() + ".xsd"; 
		String xsd_file_path;
		
		// CREATE all elements and texts nodes
		connectionSet 		  = doc.createElement("connectionSet");
		xsdConnection 		  = doc.createElement("xsdConnection");
		uri 		  		  = doc.createElement("uri");
		rootElement 		  = doc.createElement("rootElement");
		uri_text_node 		  = doc.createTextNode(file_xsd); 				//xsd file
		rootElement_text_node = doc.createTextNode("root"); 	
        
		//Attr of "connectionSet"
		connectionSet.setAttribute("xmlns", "http://www.xfa.org/schema/xfa-connection-set/2.8/");
		
		//Attr of "xsdConnection"
		xsdConnection.setAttribute(name_attr,"DataConnection");
		xsdConnection.setAttribute(dataDescription_attr,"root");
		
		//Add TEXTnode Child of "uri"
		uri.appendChild(uri_text_node);
			
		//Add TEXTnode Child of "rootElement"
		rootElement.appendChild(rootElement_text_node);
		
		//Add Children of "xsdConnection"
		xsdConnection.appendChild(uri);
		xsdConnection.appendChild(rootElement);
		
		//Add Children of "connectionSet"
		connectionSet.appendChild(xsdConnection);
		
		Node isconnectionsetPresent = null;
		
		NodeList conns = root.getElementsByTagName("connectionSet");
		if(conns.getLength() >0 ) {
			//replace existing
			isconnectionsetPresent = conns.item(0);
			root.replaceChild(connectionSet, isconnectionsetPresent);
		}else {		
		    //Add to root "connectionSet"
		    root.appendChild(connectionSet);					
		}
	  	    
		/*
		<connectionSet xmlns="http://www.xfa.org/schema/xfa-connection-set/2.8/">
		   <xsdConnection name="DataConnection" dataDescription="root">
		   		<uri>	----path-------	-----nome file-----.xsd		</uri>
      	   		<rootElement>root</rootElement>
      	   															<?templateDesigner fileDigest shaHash="wy94rRkPIXolg4RYXtK8ANyi3T8="?>
      	   </xsdConnection>
		</connectionSet>

		*/
	    /*
	    <connectionSet xmlns="http://www.xfa.org/schema/xfa-connection-set/2.8/">
	    	<xsdConnection dataDescription="root" name="DataConnection">
	    		<uri>						-----nome file-----.xsd			</uri>
	    		<rootElement>root</rootElement>
	    	</xsdConnection>
	    </connectionSet>
	    
	    */
	}

	//------------------------------------------- PUBLIC Methods --------------------------------------
	
	/* (non-Javadoc)
	 * @see docmodel.XDP_Reader_Interface#save(java.lang.String)
	 */
	@Override
	public void save(String filename)
	{
		//TODO check file name --> must have (XDP) extension
		
		DOMSource source    	 			  = new DOMSource(doc);	//DOMSource or Source
		StreamResult destination 			  = new StreamResult(new File(filename)); //StreamResult or Result
		TransformerFactory transformerFactory = TransformerFactory.newInstance();
		
		try 
		{
			Transformer tr = transformerFactory.newTransformer();
			tr.transform(source, destination);
		} 
		catch (Exception e) 
		{
			e.printStackTrace();
			log.append("\nErrore durante il salvataggio XML\n" + e.getMessage() + "\n" + e.getStackTrace());
		}		
//		
//		StreamResult destination = null;
//		try {
//			destination = new StreamResult(new BufferedOutputStream(new FileOutputStream(new File(filename))));
//		} catch (FileNotFoundException e1) {			 
//			e1.printStackTrace();
//		} //StreamResult or Result
//		TransformerFactory transformerFactory = TransformerFactory.newInstance();
//		
//
//		DOMSource source    	 = new DOMSource(doc);	//DOMSource or Source
////		StringWriter sw 		 = new StringWriter();
//		StreamResult file_dest   = new StreamResult(new File(filename)); //StreamResult or Result
////	    StreamResult screen_dest = new StreamResult(sw);
//	   
//
//		try 
//		{
//			Transformer tr = TransformerFactory.newInstance().newTransformer();
//			//tr.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
//			tr.transform(source, file_dest);
////			tr.transform(source, screen_dest);
//		} 
//		catch (Exception e) 
//		{
//			e.printStackTrace();
//			log.append("\nErrore durante il salvataggio XML\n" + e.getMessage() + "\n" + e.getStackTrace());
//		}
	
		//P
		//System.out.println(sw.toString());
	}

	/* (non-Javadoc)
	 * @see docmodel.XDP_Reader_Interface#apriFile()
	 */
	@Override
	public String apriFile() 
	{		
		//------------------------------------------------------------------
		// Java PARSER
		//------------------------------------------------------------------
		/*
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		
		//factory.setValidating(false);
		//factory.setNamespaceAware(true);
		//factory.setIgnoringElementContentWhitespace(true);
	    //factory.setExpandEntityReferences(false);
	    //factory.setIgnoringComments(true);
      	//factory.setCoalescing(true);
	    
		DocumentBuilder db;
		try 
		{
			db = factory.newDocumentBuilder();
			doc = db.parse(new File(inputFile));
		}catch (ParserConfigurationException e) {
			e.printStackTrace();
		}catch (SAXException e) {
			e.printStackTrace();
		}catch (IOException e) {
			e.printStackTrace();
		}
		*/
		
		//------------------------------------------------------------------
		// Xerces PARSER															//TODO getParser invece di "new"
		//------------------------------------------------------------------
		
		parser = new DOMParser();
        
		//FEATURES
		try
	    {
			//parser.setFeature("http://apache.org/xml/features/dom/defer-node-expansion", true);		//--OK      
			parser.setFeature("http://apache.org/xml/features/continue-after-fatal-error", false);		//--OK      
	    	//parser.setFeature("http://xml.org/sax/features/namespaces", false);						//--OK
			//parser.setFeature("http://xml.org/sax/features/validation", true);						//--ERRORE
		} catch (SAXNotRecognizedException e1) {
			e1.printStackTrace();
		} catch (SAXNotSupportedException e1) {
			e1.printStackTrace();
		}
	    
		//PARSING
		try 
		{
			parser.parse(inputFile);
		} 
	    catch (SAXException e) {
			e.printStackTrace();
		}
		catch (IOException e){
			e.printStackTrace();
		}
	    
	    doc = parser.getDocument();
	    root = doc.getDocumentElement();
		
	    ///////////////////////////////////////////////////////////////////////
		((DocumentImpl) doc).addEventListener("DOMNodeRemoved", this, false);
		EventTarget evTarget = (EventTarget) doc;
		evTarget.addEventListener("DOMNodeInserted", this, false);
		///////////////////////////////////////////////////////////////////////
		
	    return "FILE XDP aperto correttamente"; 
	}

	/* (non-Javadoc)
	 * @see docmodel.XDP_Reader_Interface#handleEvent(org.w3c.dom.events.Event)
	 */
	@Override
	public void handleEvent(Event ev)
	{
		if (ev.getType().equals(MutationEventImpl.DOM_NODE_REMOVED)) {
			Node node = (Node) ev.getTarget();
			Node parentNode = node.getParentNode();
			System.out.println("Node " + node.getNodeName()+ " was removed from " + parentNode.getNodeName());
		}
		
		if (ev.getType().equals(MutationEventImpl.DOM_NODE_INSERTED)) {
			Node node = (Node) ev.getTarget();
			Node parentNode = node.getParentNode();
			System.out.println("Node " + node.getNodeName()+ " was inserted as a child of "+ parentNode.getNodeName());
		}
	}
	
	/**
	 * 
	 * @return
	 */
	public StringBuffer getLogBuffer(){
		return log;
	}
	
	//--- USATO da Gui ------
	public XDP_Liste getListe() {
		return liste;
	}
	
	//---------------usati da XSD Tool ------------------- 
	
	/* (non-Javadoc)
	 * @see docmodel.XDP_Reader_Interface#getParser()
	 */
	@Override
	public DOMParser getParser() {
		return parser;
	}

	/* (non-Javadoc)
	 * @see docmodel.XDP_Reader_Interface#getDocument()
	 */
	@Override
	public Document getDocument() {
		return doc;
	}
	
	/* (non-Javadoc)
	 * @see docmodel.XDP_Reader_Interface#getInputFile()
	 */
	@Override
	public String getInputFile() {
		 
		return inputFile;
	}
	
	//--------------------------------------------------
	
	/* (non-Javadoc)
	 * @see docmodel.XDP_Reader_Interface#run()
	 */
	@Override
	public String run () 
	{
		// (0)
		apriFile();								//TODO senza parametro!! scrivere codice riusabile!
		
		// (1) 
		try 
		{
			scorriNodo(root);
		} 
		catch (Exception e) 
		{
			log.append("Errore durante la lettura XML (file vuoto?)\n" + e.getMessage() + "\n");
			e.printStackTrace();
			return "lettura XDP terminata con errore";
		}
		
		// (2) - ADD floatingFields
		add_FloatingFields();					//TODO senza parametro!! scrivere codice riusabile!
		
		// (3) - scrivi XSD file
		xsd_tool.run();
		
		// (4)
		add_ConnectionSet();					//TODO senza parametro!! scrivere codice riusabile!
					
		// (5) 
		correct_color(root);
		
		return "End of run";
	}
	

	/**
	 * 
	 * @param main
	 */
	private void correct_color(Element main) {
		
		NodeList colors = main.getElementsByTagName("color");
		for(int i=0; i < colors.getLength(); i++) {
			Node color=colors.item(i);
			NamedNodeMap attrs = color.getAttributes();
			Node value= attrs.getNamedItem("value");
			if(value.getNodeValue().equals("255,0,0")){
				value.setNodeValue("0,0,0");
			}
		}
	  	    	
		//style="color:#ff0000;font-weight:bold"
		
		NodeList spans = main.getElementsByTagName("span");
		for(int i=0; i < spans.getLength(); i++) {
			Node span=spans.item(i);
			NamedNodeMap attrs = span.getAttributes();
			Node style= attrs.getNamedItem("style");
			if(style!=null && style.getNodeValue().contains("color:#ff0000;")){
				style.setNodeValue("");
			}
		}
	}


	/**
	 * @param args
	 */
	public static void main(String[] args) 
	{
//		//String inputFile  = "dds_ae_stagional_tv.xdp";		
//		String inputFile = "test4.xdp";
//		//String inputFile = "prova.xdp";
//		String outputFile = "simpleout.xdp";
//	
//		XDP_Reader_Interface xdp = new XDP_Reader(inputFile);		
//		xdp.apriFile();
//		xdp.run(); 
//		xdp.save(outputFile);
//		
//		//P
//		System.out.println(xdp.getLogBuffer());
//		
//		//P
//		//xdp.liste.stampa_lista();	
	}
}
